home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue60 / COMThrd / STAThread.pas < prev   
Encoding:
Pascal/Delphi Source File  |  2000-06-07  |  6.3 KB  |  225 lines

  1. unit STAThread;
  2.  
  3. interface
  4.  
  5. uses
  6.   ComObj, ActiveX, Classes, Windows;
  7.  
  8. type
  9.   TComObjectFactory2 = class(TComObjectFactory, IClassFactory)
  10.   protected
  11.     //Create the COM object in a separate thread
  12.     function CreateInstance(const UnkOuter: IUnknown;
  13.       const IID: TGUID; out Obj): HResult; stdcall;
  14.   end;
  15.  
  16.   TTypedComObjectFactory2 = class(TTypedComObjectFactory, IClassFactory)
  17.   protected
  18.     //Create the COM object in a separate thread
  19.     function CreateInstance(const UnkOuter: IUnknown;
  20.       const IID: TGUID; out Obj): HResult; stdcall;
  21.   end;
  22.  
  23.   TAutoObjectFactory2 = class(TAutoObjectFactory, IClassFactory)
  24.   protected
  25.     //Create the Automation object in a separate thread
  26.     function CreateInstance(const UnkOuter: IUnknown;
  27.       const IID: TGUID; out Obj): HResult; stdcall;
  28.   end;
  29.  
  30.   TApartmentThread = class(TThread)
  31.   private
  32.     FFactory: IClassFactory2;
  33.     FUnkOuter: IUnknown;
  34.     FIID: TGuid;
  35.     FSemaphore: THandle;
  36.     FStream: Pointer;
  37.     FCreateResult: HResult;
  38.   protected
  39.     procedure Execute; override;
  40.   public
  41.     constructor Create(Factory: IClassFactory2;
  42.       UnkOuter: IUnknown; IID: TGuid);
  43.     destructor Destroy; override;
  44.     property Semaphore: THandle read FSemaphore;
  45.     property CreateResult: HResult read FCreateResult;
  46.     property ObjStream: Pointer read FStream;
  47.   end;
  48.  
  49. implementation
  50.  
  51. uses
  52.   SysUtils;
  53.  
  54. { TComObjectFactory2 }
  55.  
  56. function TComObjectFactory2.CreateInstance(const UnkOuter: IUnknown;
  57.   const IID: TGUID; out Obj): HResult;
  58. begin
  59.   //Verify we are not an in-proc server and that the object is STA-ready
  60.   if not IsLibrary and (ThreadingModel = tmApartment) then
  61.   begin
  62.     LockServer(True);
  63.     try
  64.       //Create thread
  65.       with TApartmentThread.Create(Self, UnkOuter, IID) do
  66.       begin
  67.         //Wait for thread to create the COM object
  68.         if WaitForSingleObject(Semaphore, INFINITE) = WAIT_OBJECT_0 then
  69.         begin
  70.           Result := CreateResult;
  71.           if Result <> S_OK then Exit;
  72.           //If all is well, unmarshal the interface from the stream
  73.           Result := CoGetInterfaceAndReleaseStream(IStream(ObjStream), IID, Obj);
  74.         end
  75.         else
  76.           Result := E_FAIL
  77.       end
  78.     finally
  79.       LockServer(False)
  80.     end
  81.   end
  82.   else
  83.     //In-proc servers and non-STA objects get created as normal
  84.     Result := inherited CreateInstance(UnkOuter, IID, Obj);
  85. end;
  86.  
  87. { TTypedComObjectFactory2 }
  88.  
  89. function TTypedComObjectFactory2.CreateInstance(const UnkOuter: IUnknown;
  90.   const IID: TGUID; out Obj): HResult;
  91. begin
  92.   //Verify we are not an in-proc server and that the object is STA-ready
  93.   if not IsLibrary and (ThreadingModel = tmApartment) then
  94.   begin
  95.     LockServer(True);
  96.     try
  97.       //Create thread
  98.       with TApartmentThread.Create(Self, UnkOuter, IID) do
  99.       begin
  100.         //Wait for thread to create the COM object
  101.         if WaitForSingleObject(Semaphore, INFINITE) = WAIT_OBJECT_0 then
  102.         begin
  103.           Result := CreateResult;
  104.           if Result <> S_OK then Exit;
  105.           //If all is well, unmarshal the interface from the stream
  106.           Result := CoGetInterfaceAndReleaseStream(IStream(ObjStream), IID, Obj);
  107.         end
  108.         else
  109.           Result := E_FAIL
  110.       end
  111.     finally
  112.       LockServer(False)
  113.     end
  114.   end
  115.   else
  116.     //In-proc servers and non-STA objects get created as normal
  117.     Result := inherited CreateInstance(UnkOuter, IID, Obj);
  118. end;
  119.  
  120. { TAutoObjectFactory2 }
  121.  
  122. function TAutoObjectFactory2.CreateInstance(const UnkOuter: IUnknown;
  123.   const IID: TGUID; out Obj): HResult;
  124. begin
  125.   //Verify we are not an in-proc server and that the object is STA-ready
  126.   if not IsLibrary and (ThreadingModel = tmApartment) then
  127.   begin
  128.     LockServer(True);
  129.     try
  130.       //Create thread
  131.       with TApartmentThread.Create(Self, UnkOuter, IID) do
  132.       begin
  133.         //Wait for thread to create the COM object
  134.         if WaitForSingleObject(Semaphore, INFINITE) = WAIT_OBJECT_0 then
  135.         begin
  136.           Result := CreateResult;
  137.           if Result <> S_OK then Exit;
  138.           //If all is well, unmarshal the interface from the stream
  139.           Result := CoGetInterfaceAndReleaseStream(IStream(ObjStream), IID, Obj);
  140.         end
  141.         else
  142.           Result := E_FAIL
  143.       end
  144.     finally
  145.       LockServer(False)
  146.     end
  147.   end
  148.   else
  149.     //In-proc servers and non-STA objects get created as normal
  150.     Result := inherited CreateInstance(UnkOuter, IID, Obj);
  151. end;
  152.  
  153. { TApartmentThread }
  154.  
  155. constructor TApartmentThread.Create(Factory: IClassFactory2;
  156.   UnkOuter: IUnknown; IID: TGuid);
  157. begin
  158.   inherited Create(True);
  159.   FFactory := Factory;
  160.   FUnkOuter := UnkOuter;
  161.   FIID := IID;
  162.   //Create the synchronisation device
  163.   FSemaphore := CreateSemaphore(nil, 0, 1, nil);
  164.   FreeOnTerminate := True;
  165.   //After setting all the thread attributes, let this thread start
  166.   Resume
  167. end;
  168.  
  169. destructor TApartmentThread.Destroy;
  170. begin
  171.   CloseHandle(FSemaphore);
  172.   inherited Destroy;
  173. end;
  174.  
  175. procedure TApartmentThread.Execute;
  176. var
  177.   Msg: TMsg;
  178.   Unk: IUnknown;
  179.  
  180.   function FinalRefCount: Integer;
  181.   begin
  182.     //Return 0 on Win95 (Windows version 4.0)
  183.     if (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
  184.        (Win32MajorVersion = 4) and (Win32MinorVersion = 0) then
  185.       Result := 0
  186.     else
  187.       Result := 1
  188.   end;
  189.  
  190. begin
  191.   try
  192.     //Enter STA
  193.     CoInitialize(nil);
  194.     try
  195.       //Create object
  196.       FCreateResult := FFactory.CreateInstanceLic(FUnkOuter, nil, FIID, '', Unk);
  197.       FUnkOuter := nil;
  198.       FFactory := nil;
  199.       //Marshal interface reference into stream
  200.       if FCreateResult = S_OK then
  201.         CoMarshalInterThreadInterfaceInStream(FIID, Unk, IStream(FStream));
  202.       //Allow factory to read the interface reference
  203.       ReleaseSemaphore(FSemaphore, 1, nil);
  204.       if FCreateResult = S_OK then
  205.         //Start the message pump
  206.         while GetMessage(Msg, 0, 0, 0) do
  207.         begin
  208.           DispatchMessage(Msg);
  209.           //See if the only connection to this object is ours
  210.           //If it is, then this thread's work is done
  211.           Unk._AddRef;
  212.           if Unk._Release = FinalRefCount then
  213.             Break;
  214.         end;
  215.     finally
  216.       Unk := nil;
  217.       //Leave the STA
  218.       CoUninitialize;
  219.     end;
  220.   except
  221.     // No exceptions should go unhandled
  222.   end;
  223. end;
  224.  
  225. end.